跳到主要内容

在Python中使用.NET的反射特性

· 阅读需 3 分钟
Kyoku⭐==
梦想是成为可以每天睡满10小时的人。

RT,最近在研究从.NET 生成的动态链接库里提取需要的常量(比如一些加密 Key),用 Python 可以实现一定程度的自动化。

使用反射需要你知道常量原本的命名空间,如果不知道可以用ILSpydnSpydotPeek这类工具先手动反编译找一下。

什么你问我为什么都已经反编译出来找了,不如直接复制常量出来用?盲生你好像发现了华点......
主要是要通过 Python 来实现动态链接以及自动化读取,如果常量经常发生变化或者有特殊需求就很有点用了。

总而言之,先上一段 dotPeek 反编译出来的 C#代码作为示例:

using XXX2.Util;
using System.Security.Cryptography;
using System.Text;

#nullable disable
namespace Net
{
internal class CipherAES : Singleton<CipherAES>
{
private static readonly int BLOCK_SIZE = 128;
private static readonly int KEY_SIZE = 256;
private static readonly string AesKey = "This is an AesKey";
private static readonly string AesIV = "This is an AesIv";

public static byte[] Encrypt(byte[] data)
{
// ......
}

public static byte[] Decrypt(byte[] encryptData)
{
// ......
}
}
}

我们的目标是通过 Python 脚本自动获取其中的加密常量用于自己的仿真客户端,这时我们需要使用pythonnet这个库:

poetry add pythonnet

新建一个.py脚本,随便叫什么都行,我给个peek.py吧。

# 引入pythonnet以及System包
import clr
# 因为IDE一般没法直接认clr加载进来的.net库,所以可以加上下面这行把IDE标红消掉,眼不见为净。
# noinspection PyUnresolvedReferences
from System import Reflection

assembly = Reflection.Assembly.LoadFile("X:\xxx\xxx\xxx.dll") # 必须使用绝对路径!

我现在已经知道了我要的常量存在于Net.CipherAES类中,那么直接对其进行 GetType

cipher_aes_type = assembly.GetType('Net.CipherAES')

然后就可以利用 GetField 方法查找常量所在的位置了:

if cipher_aes_type is not None:  # 可以通过这个方法来判断是否能够定位这个类
# 获取常量值
aes_key = cipher_aes_type.GetField('AesKey', Reflection.BindingFlags.Static | Reflection.BindingFlags.NonPublic).GetValue(None)

完整代码:

assembly = Reflection.Assembly.LoadFile("X:\xxx\xxx\xxx.dll")
try:
cipher_aes_type = assembly.GetType('Net.CipherAES')
if cipher_aes_type is not None:
print(f"Found type: {cipher_aes_type.FullName}")

# 获取常量值
block_size = cipher_aes_type.GetField('BLOCK_SIZE',
Reflection.BindingFlags.Static | Reflection.BindingFlags.NonPublic).GetValue(
None)
key_size = cipher_aes_type.GetField('KEY_SIZE',
Reflection.BindingFlags.Static | Reflection.BindingFlags.NonPublic).GetValue(
None)
aes_key = cipher_aes_type.GetField('AesKey',
Reflection.BindingFlags.Static | Reflection.BindingFlags.NonPublic).GetValue(
None)
aes_iv = cipher_aes_type.GetField('AesIV',
Reflection.BindingFlags.Static | Reflection.BindingFlags.NonPublic).GetValue(
None)

print(f"BLOCK_SIZE: {block_size}")
print(f"KEY_SIZE: {key_size}")
print(f"AesKey2: {aes_key}")
print(f"AesIV: {aes_iv}")
except Exception as e:
print(f"Exception occurred: {e}")